/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xdlink.component.channel.impl.bt;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;

import com.xuexiang.xdlink.component.channel.IChannel;
import com.xuexiang.xdlink.component.channel.OnChannelListener;
import com.xuexiang.xdlink.component.channel.entity.ConnectStatusEnum;
import com.xuexiang.xdlink.component.channel.impl.AbstractChannel;
import com.xuexiang.xdlink.component.channel.impl.AbstractDataReceiver;
import com.xuexiang.xdlink.exception.ChannelException;
import com.xuexiang.xdlink.logs.DCFLog;
import com.xuexiang.xdlink.util.ConvertTools;
import com.xuexiang.xdlink.util.DCFExecutors;
import com.xuexiang.xdlink.util.Utils;

import java.io.IOException;
import java.util.UUID;

import static com.xuexiang.xdlink.exception.ChannelException.WRITE_CHANNEL_CLOSED_ERROR;
import static com.xuexiang.xdlink.exception.ChannelException.WRITE_FAILED_ERROR;

/**
 * 经典蓝牙设备通信通道
 *
 * @author xuexiang
 * @since 2018/8/18 上午12:57
 */
public class BluetoothChannel extends AbstractChannel {

    /**
     * 蓝牙串口服务
     */
    private static final UUID SPS_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    /**
     * 系统蓝牙适配器
     */
    private BluetoothAdapter mBluetoothAdapter;

    private BluetoothSocket mBluetoothSocket;
    /**
     * 蓝牙地址
     */
    private String mMacAddress;

    /**
     * 构造方法
     *
     * @param macAddress 蓝牙地址
     */
    public BluetoothChannel(String macAddress) {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mMacAddress = macAddress;
    }

    @Override
    public void openChannel() {
        if (isOpen()) return;

        DCFExecutors.get().addTask(new Runnable() {
            @Override
            public void run() {
                startConnect(mMacAddress);
            }
        });
    }

    /**
     * 开始连接蓝牙设备，获取socket
     */
    private void startConnect(String address) {
        BluetoothDevice device = getBluetoothDevice(address);
        if (isBlueToothBond(device)) {
            try {
                mBluetoothSocket = device.createRfcommSocketToServiceRecord(SPS_UUID);
                if (!mBluetoothSocket.isConnected()) {
                    mBluetoothSocket.connect();
                }
                handleConnectResult(true);
            } catch (Exception e) {
                e.printStackTrace();
                DCFLog.e("Bluetooth device:" + address + "connect failed!", e);
                handleConnectResult(false);
            }
        } else {
            DCFLog.e("Bluetooth device:" + address + "connect failed：Bluetooth device not matched");
            handleConnectResult(false);
        }
    }

    /**
     * 处理连接结果
     *
     * @param isConnectSuccess 是否连接成功
     */
    private void handleConnectResult(boolean isConnectSuccess) {
        if (isConnectSuccess) {
            DCFLog.i("Bluetooth device:" + mMacAddress + "connect success...");
            if (isOpen()) {
                startAcceptData(new BluetoothDataReceiver(getChannelListener()));
            }
            updateConnectStatus(ConnectStatusEnum.CONNECT_SUCCESS);// 打开设备成功,发送命令
        } else {
            updateConnectStatus(ConnectStatusEnum.CONNECT_FAILED);
        }
    }


    @Override
    public boolean isOpen() {
        return mBluetoothSocket != null && mBluetoothSocket.isConnected();
    }

    @Override
    public void closeChannel() {
        close();
    }

    /**
     * 关闭蓝牙通道
     */
    @Override
    protected void close() {
        super.close();
        Utils.closeIO(mBluetoothSocket);
        mBluetoothSocket = null;
    }

    @Override
    public void write(byte[] message) throws ChannelException {
        if (isOpen()) {
            try {
                mBluetoothSocket.getOutputStream().write(message);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ChannelException(WRITE_FAILED_ERROR, e);
            }
        } else {
            throw new ChannelException(WRITE_CHANNEL_CLOSED_ERROR, "Bluetooth channel closed, message sent failed：" + ConvertTools.bytes2HexString(message));
        }
    }

    /**
     * 蓝牙数据接收器
     *
     * @author xuexiang
     * @since 2018/8/16 上午12:54
     */
    private class BluetoothDataReceiver extends AbstractDataReceiver {

        BluetoothDataReceiver(OnChannelListener listener) {
            super(listener);
        }

        @Override
        protected void onStart() throws IOException {
            if (mBluetoothSocket != null) {
                mInputStream = mBluetoothSocket.getInputStream();
            }
        }

        @Override
        public IChannel getChannel() {
            return BluetoothChannel.this;
        }
    }

    //========================================蓝牙操作====================================//

    /**
     * 蓝牙是否已绑定
     *
     * @param address
     * @return
     */
    public boolean isBlueToothBond(String address) {
        BluetoothDevice device = getBluetoothDevice(address);
        return isBlueToothBond(device);
    }

    /**
     * 蓝牙是否已绑定
     *
     * @param device
     * @return
     */
    private boolean isBlueToothBond(BluetoothDevice device) {
        return device != null && device.getBondState() == BluetoothDevice.BOND_BONDED;
    }

    /**
     * 根据地址获取蓝牙设备
     *
     * @param address
     * @return
     */
    private BluetoothDevice getBluetoothDevice(String address) {
        if (mBluetoothAdapter != null) {
            return mBluetoothAdapter.getRemoteDevice(address);
        } else {
            return BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address);
        }
    }

}
